﻿using log4net;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Windows;
using WpfApp1.Acme.ApplicationAttribute;
using WpfApp1.Acme.ApplicationClass;
using WpfApp1.Acme.Template;

namespace WpfApp1.Acme.TemplateImpl {
    /// <summary>
    /// ExecutionAttribute负责
    /// 特性的功能执行
    /// </summary>
    /// <typeparam name="A"></typeparam>
    public static class ExecutionAttribute {
        /** 在App.config配置文件中读取DataSourceConnectionString配置项
         * 配置如下：
         * <connectionStrings>
         *	<add name="DataSourceConnectionString" connectionString="数据源配置字符串"/>
         * </connectionStrings>
         * obj?.ToString();
         * 中的?.相对于:
         * obj == null ? null : obj;
         * */
        private static string connString = ConfigurationManager.ConnectionStrings["DataSourceConnectionString"]?.ConnectionString;
        /** 日志对象 */
        private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        /** 两个参数的特性策略池 */
        private static readonly Dictionary<Type, Func<Assembly, Attribute, DataSourceOperation>> attributeTwoStrategy = new Dictionary<Type, Func<Assembly, Attribute, DataSourceOperation>>();
        /** 三个参数的特性策略池 */
        private static readonly Dictionary<Type, Action<Assembly, Attribute, string>> attributeThreeStrategy = new Dictionary<Type, Action<Assembly, Attribute, string>>();
        /** 五个参数的特性策略池 */
        private static readonly Dictionary<Type, Action<Type, Assembly, string, string, Type>> attributeFiveStrategy = new Dictionary<Type, Action<Type, Assembly, string, string, Type>>();
        /** 用于合并两个数组的List类 */
        private static readonly List<string> list = new List<string>();
        /** 扫描框架内部指定的包 */
        private static readonly string[] acmePack = {
            "Acme.DataSourcePackage"
        };
        static ExecutionAttribute() {
            attributeTwoStrategy.Add(typeof(string), (assembly, attribute)=> { return null; });
            attributeThreeStrategy.Add(typeof(string), (assembly, attribute, fullName)=> { });
            attributeFiveStrategy.Add(typeof(string), (viewDataClass, assembly, beanName, containerName, viewPageClass) => { });

            attributeTwoStrategy.Add(typeof(ComponentScan), componentScanExecute);
            attributeTwoStrategy.Add(typeof(DataSource), dataSourceExecute);

            attributeThreeStrategy.Add(typeof(Component), componentExecute);
            attributeThreeStrategy.Add(typeof(ViewData), viewDataExecute);

            attributeFiveStrategy.Add(typeof(ViewPage), viewPageExecute);
        }
        private static DataSourceOperation componentScanExecute(Assembly assembly, Attribute componentScan) {
            // 获取ComponentScan特性中的包路径并合并acmePack
            string[] packagePaths = (string[])componentScan.GetType().GetProperty("PackagePaths").GetValue(componentScan);
            list.AddRange(packagePaths);
            list.AddRange(acmePack);

            list.ForEach((stringFormat) => {
                string packagePath = "\\" + stringFormat.Replace(".", "\\");
                string currentPath = Environment.CurrentDirectory;
                currentPath = currentPath.Substring(0, currentPath.LastIndexOf("\\") - 4);
                // 扫描指定包
                ApplicationBeanDefinitionScanner.get().scanning(currentPath + packagePath, assembly);
            });
            return null;
        }

        public static DataSourceOperation dataSourceExecute(Assembly assembly, Attribute dataSource) {
            // 获取连接数据库的连接类
            Type dataSourceType = (Type)dataSource.GetType().GetProperty("DataClass").GetValue(dataSource);
            logger.Info("正连接数据库：" + dataSourceType.FullName);
            DataSourceOperation dataSourceOperation = DataSourceContext.getStraytegy(dataSourceType.Name);

            // 实例化数据源操作对象并连接数据库
            if (dataSourceOperation is not null) {
                try {
                    bool isConnection = dataSourceOperation.connection(Activator.CreateInstance(dataSourceType, connString));
                    logger.Info("是否连接数据库成功：" + isConnection);
                } catch (Exception ex) {
                    logger.Error(ex);
                }
            }
            return dataSourceOperation;
        }

        private static void componentExecute(Assembly assembly, Attribute attribute, string fullName) {
            // 扫描到Component时创建一个BeanDefinition对象
            BeanDefinition beanDefinition = new BeanDefinition();
            Type type = assembly.GetType(fullName);
            beanDefinition.BeanType = type;

            // 获取bean的名称
            string beanName;
            if ("".Equals(attribute.GetType().GetProperty("BeanName").GetValue(attribute))) {
                beanName = fullName.Substring(fullName.LastIndexOf(".") + 1);
                beanName = char.ToLower(beanName[0]) + beanName.Substring(1);
            } else {
                beanName = (string)attribute.GetType().GetProperty("BeanName").GetValue(attribute);
            }

            // 是否使用了Scope特性，如果没有则默认为singleton
            attribute = type.GetCustomAttribute(typeof(Scope));
            if (attribute is not null) {
                beanDefinition.Scope = (ScopeType)attribute.GetType().GetProperty("ScopeValue").GetValue(attribute);
            }

            // 注册BeanDefinition
            attribute = type.GetCustomAttribute(typeof(Lazy));
            bool isNoNullChar = (beanName is not null);
            bool beanDefinitionIsNoNull = (beanDefinition is not null);
            if (isNoNullChar && beanDefinitionIsNoNull) {
                beanDefinition.IsLazy = (attribute == null ? false : true);
                bool isNoContainsKey = !BeanFactory.getBeanDefinitionMap().ContainsKey(beanName);
                if (isNoContainsKey) {
                    logger.Info("正在注册: " + type);
                    if (typeof(BeanInitializing).IsAssignableFrom(type)) {
                        ((BeanInitializing)ApplicationContext.getExecutingAssembly().CreateInstance(beanDefinition.BeanType.ToString())).beanInitializingBefore();
                    }
                    BeanFactory.getBeanDefinitionMap().TryAdd(beanName, beanDefinition);
                }
            }

            // 判断是否实现BeanInformation接口
            if (typeof(BeanInformation).IsAssignableFrom(type)) {
                ((BeanInformation)ApplicationContext.getExecutingAssembly().CreateInstance(type.FullName)).setBeanName(beanName);
            }
        }

        private static void viewDataExecute(Assembly assembly, Attribute attribute, string fullName) {
            // 创建Bean定义类并获取ViewData指定的容器名
            Type type = assembly.GetType(fullName);
            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.BeanType = assembly.GetType(fullName);
            string beanName = (string)attribute.GetType().GetProperty("ContainerName").GetValue(attribute);

            // 注册视图数据类
            bool isNoNullChar = (beanName is not null);
            bool beanDefinitionIsNoNull = (beanDefinition is not null);
            if (isNoNullChar && beanDefinitionIsNoNull) {
                beanDefinition.Scope = ScopeType.singleton;
                beanDefinition.IsLazy = (attribute == null ? false : true);
                bool isNoContainsKey = !BeanFactory.getBeanDefinitionMap().ContainsKey(beanName);
                if (isNoContainsKey) {
                    logger.Info("注册视图数据类：" + assembly.GetType(fullName));
                    BeanFactory.getBeanDefinitionMap().TryAdd(beanName, beanDefinition);
                }
            }

            // 获取视图路径
            string viewPath = (string)attribute.GetType().GetProperty("ViewPath").GetValue(attribute);
            string containerName = (string)attribute.GetType().GetProperty("ContainerName").GetValue(attribute);
            if (viewPath != "") {
                Type view = assembly.GetType(viewPath);
                Attribute otherViewPage = view.GetCustomAttribute(typeof(ViewPage));
                attributeFiveStrategy[otherViewPage.GetType() ?? typeof(string)](type, assembly, beanName, containerName, view);

            } else {
                // 获取主视图
                Type mainWin = assembly.GetType(ApplicationContext.getExecutingAssembly().GetName().Name + ".MainWindow");
                Attribute mainViewPage = mainWin.GetCustomAttribute(typeof(ViewPage));
                if (mainWin is not null) { logger.Info("扫描到主视图: " + mainWin); }
                // 当mainViewPage为null时使用typeof(string)
                attributeFiveStrategy[mainViewPage.GetType() ?? typeof(string)](type, assembly, beanName, containerName, mainWin);
            }
        }

        private static void viewPageExecute(Type viewDataClass, Assembly assembly, string beanName, string containerName, Type viewPageClass) {
            try {
                // 获取指定类上的ViewPage特性
                Attribute attribute = viewPageClass.GetCustomAttribute(typeof(ViewPage));
                // 获取当前类的属性
                FieldInfo[] fieldInfos = viewDataClass.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
                foreach (FieldInfo fieldInfo in fieldInfos) {
                    ViewTwoWayBinding.get().onPropertyChanged(char.ToUpper(fieldInfo.Name[0]) + fieldInfo.Name.Substring(1));
                }

                // 如果是主视图则绑定主视图中的DataContext的数据类
                if (viewPageClass == typeof(MainWindow)) {
                    Application.Current.MainWindow.FindName(containerName).GetType().GetProperty("DataContext").SetValue(Application.Current.MainWindow, ApplicationContext.getBean(beanName));
                } else {
                    // 其他视图则创建BeanDefinition在进行管理
                    BeanDefinition beanDefinition = new BeanDefinition();
                    string viewBeanName = (char.ToLower(viewPageClass.Name[0]) + viewPageClass.Name.Substring(1));
                    beanDefinition.BeanType = viewPageClass;
                    beanDefinition.Scope = (ScopeType)attribute.GetType().GetProperty("ViewPageScopeType").GetValue(attribute);
                    beanDefinition.IsLazy = false;
                    BeanFactory.getBeanDefinitionMap().TryAdd(viewBeanName, beanDefinition);
                }
            } catch (Exception e) {
                logger.Error(e);
            }
        }

        public static Dictionary<Type, Func<Assembly, Attribute, DataSourceOperation>> getAttributeTwoStrategy() {
            return attributeTwoStrategy;
        }

        public static Dictionary<Type, Action<Assembly, Attribute, string>> getAttributeThreeStrategy() {
            return attributeThreeStrategy;
        }
        public static Dictionary<Type, Action<Type, Assembly, string, string, Type>> getAttributeFiveStrategy() { 
            return attributeFiveStrategy; 
        }
    }
}